/*
 * SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson
 * SPDX-License-Identifier: GPL-3.0-only
 */

package com.sanji.audio_test.rule

import android.Manifest
import android.app.Application
import android.content.pm.PackageManager
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.sanji.audio_test.ContactGroupInfo
import com.sanji.audio_test.ContactInfo
import com.sanji.audio_test.GroupLookup
import com.sanji.audio_test.Preferences
import com.sanji.audio_test.getContactByLookupKey
import com.sanji.audio_test.getContactGroupById
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext

/**
 * 可显示的规则数据类
 * 包含规则列表以及相关的联系人和联系人组信息
 * @param rules 规则列表
 * @param contacts 联系人映射
 * @param groups 联系人组映射
 */
data class DisplayableRules(
    val rules: List<RecordRule>,
    val contacts: Map<String, ContactInfo?>,
    val groups: Map<Long, ContactGroupInfo?>,
)

/**
 * 录音规则ViewModel
 * 负责管理录音规则的数据和业务逻辑
 * @param application 应用程序实例
 */
class RecordRulesViewModel(application: Application) : AndroidViewModel(application) {
    // 偏好设置实例
    private val prefs = Preferences(getApplication())

    // 规则状态流
    private val _rules = MutableStateFlow(DisplayableRules(emptyList(), emptyMap(), emptyMap()))
    val rules = _rules.asStateFlow()

    // 规则访问互斥锁
    private val rulesMutex = Mutex()

    // 初始化时刷新规则
    init {
        refreshRules()
    }

    /**
     * 刷新规则数据
     */
    private fun refreshRules() {
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                rulesMutex.withLock {
                    val rules = prefs.recordRules ?: Preferences.DEFAULT_RECORD_RULES
                    val contacts = hashMapOf<String, ContactInfo?>()
                    val groups = hashMapOf<Long, ContactGroupInfo?>()

                    // 遍历所有规则，获取相关的联系人和联系人组信息
                    for (rule in rules) {
                        when (val n = rule.callNumber) {
                            is RecordRule.CallNumber.Contact -> {
                                if (n.lookupKey !in contacts) {
                                    contacts[n.lookupKey] = getContact(n.lookupKey)
                                }
                            }
                            is RecordRule.CallNumber.ContactGroup -> {
                                // 我们在这里不持久化任何内容。按行ID键控就足够了。
                                if (n.rowId !in groups) {
                                    groups[n.rowId] = getContactGroup(n.rowId, n.sourceId)
                                }
                            }
                            else -> {}
                        }
                    }

                    _rules.update { DisplayableRules(rules, contacts, groups) }
                }
            }
        }
    }

    /**
     * 根据查找键获取联系人信息
     * @param lookupKey 联系人查找键
     * @return 联系人信息，如果获取失败则返回null
     */
    private fun getContact(lookupKey: String): ContactInfo? {
        if (getApplication<Application>().checkSelfPermission(Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
            return null
        }

        return try {
            getContactByLookupKey(getApplication(), lookupKey)
        } catch (e: Exception) {
            Log.w(TAG, "查找联系人失败", e)
            null
        }
    }

    /**
     * 根据ID获取联系人组信息
     * @param rowId 联系人组行ID
     * @param sourceId 联系人组源ID
     * @return 联系人组信息，如果获取失败则返回null
     */
    private fun getContactGroup(rowId: Long, sourceId: String?): ContactGroupInfo? {
        if (getApplication<Application>().checkSelfPermission(Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
            return null
        }

        val groupLookup = if (sourceId != null) {
            GroupLookup.SourceId(sourceId)
        } else {
            GroupLookup.RowId(rowId)
        }

        return try {
            getContactGroupById(getApplication(), groupLookup)
        } catch (e: Exception) {
            Log.w(TAG, "查找联系人组失败", e)
            null
        }
    }

    /**
     * 添加新规则
     * @param newRule 新规则
     */
    fun addRule(newRule: RecordRule) {
        val newRules = arrayListOf(newRule)
        newRules.addAll(rules.value.rules)
        replaceRules(newRules)
    }

    /**
     * 替换指定位置的规则
     * @param position 规则位置
     * @param newRule 新规则
     */
    fun replaceRule(position: Int, newRule: RecordRule) {
        val newRules = rules.value.rules.mapIndexed { i, rule ->
            if (i == position) {
                newRule
            } else {
                rule
            }
        }
        replaceRules(newRules)
    }

    /**
     * 替换所有规则
     * @param newRules 新规则列表
     */
    fun replaceRules(newRules: List<RecordRule>) {
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                rulesMutex.withLock {
                    if (newRules == Preferences.DEFAULT_RECORD_RULES) {
                        Log.d(TAG, "新规则与默认规则匹配；清除显式设置")
                        prefs.recordRules = null
                    } else {
                        Log.d(TAG, "新规则: $newRules")
                        prefs.recordRules = newRules
                    }

                    refreshRules()
                }
            }
        }
    }

    /**
     * 重置规则为默认值
     */
    fun reset() {
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                rulesMutex.withLock {
                    prefs.recordRules = null
                    refreshRules()
                }
            }
        }
    }

    // 伴生对象
    companion object {
        // 日志标签
        private val TAG = RecordRulesViewModel::class.java.simpleName
    }
}